import pytest
import asyncio
from typing import AsyncGenerator
from httpx import AsyncClient
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
from sqlalchemy.pool import StaticPool

from app.main import app
from app.models.database import Base
from app.database.connection import get_db_session
from app.dependencies import get_current_user
from app.models.database import User
from app.services.auth_service import AuthService

# Test database URL (SQLite in memory)
TEST_DATABASE_URL = "sqlite+aiosqlite:///:memory:"

# Create test engine
test_engine = create_async_engine(
    TEST_DATABASE_URL,
    connect_args={"check_same_thread": False},
    poolclass=StaticPool,
)

# Create test session factory
TestSessionLocal = async_sessionmaker(
    test_engine,
    class_=AsyncSession,
    expire_on_commit=False
)

@pytest.fixture(scope="session")
def event_loop():
    """Create an instance of the default event loop for the test session."""
    loop = asyncio.get_event_loop_policy().new_event_loop()
    yield loop
    loop.close()

@pytest.fixture
async def db_session() -> AsyncGenerator[AsyncSession, None]:
    """Create a test database session."""
    # Create tables
    async with test_engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)
    
    # Create session
    async with TestSessionLocal() as session:
        yield session
    
    # Drop tables
    async with test_engine.begin() as conn:
        await conn.run_sync(Base.metadata.drop_all)

@pytest.fixture
async def client(db_session: AsyncSession) -> AsyncGenerator[AsyncClient, None]:
    """Create a test client."""
    
    # Override database dependency
    async def override_get_db():
        yield db_session
    
    app.dependency_overrides[get_db_session] = override_get_db
    
    async with AsyncClient(app=app, base_url="http://test") as ac:
        yield ac
    
    # Clean up
    app.dependency_overrides.clear()

@pytest.fixture
async def test_user(db_session: AsyncSession) -> User:
    """Create a test user."""
    auth_service = AuthService()
    
    user = User(
        username="testuser",
        email="test@example.com",
        full_name="Test User",
        hashed_password=auth_service.get_password_hash("testpassword"),
        role="user",
        is_active=True
    )
    
    db_session.add(user)
    await db_session.commit()
    await db_session.refresh(user)
    
    return user

@pytest.fixture
async def admin_user(db_session: AsyncSession) -> User:
    """Create a test admin user."""
    auth_service = AuthService()
    
    user = User(
        username="admin",
        email="admin@example.com",
        full_name="Admin User",
        hashed_password=auth_service.get_password_hash("adminpassword"),
        role="admin",
        is_active=True
    )
    
    db_session.add(user)
    await db_session.commit()
    await db_session.refresh(user)
    
    return user

@pytest.fixture
async def auth_headers(test_user: User) -> dict:
    """Create authentication headers for test user."""
    auth_service = AuthService()
    access_token = auth_service.create_access_token(
        data={"sub": test_user.id, "username": test_user.username}
    )
    
    return {"Authorization": f"Bearer {access_token}"}

@pytest.fixture
async def admin_headers(admin_user: User) -> dict:
    """Create authentication headers for admin user."""
    auth_service = AuthService()
    access_token = auth_service.create_access_token(
        data={"sub": admin_user.id, "username": admin_user.username}
    )
    
    return {"Authorization": f"Bearer {access_token}"}

@pytest.fixture
def mock_crawl_service():
    """Mock crawl service for testing."""
    from app.services.crawl_service import MockCrawlService
    return MockCrawlService()

# Test data fixtures
@pytest.fixture
def sample_task_config():
    """Sample task configuration for testing."""
    return {
        "name": "Test Task",
        "description": "A test crawling task",
        "urls": ["https://example.com"],
        "strategy": "basic",
        "max_depth": 2,
        "delay_min": 1.0,
        "delay_max": 2.0,
        "concurrent_requests": 3,
        "timeout": 30,
        "retry_attempts": 2,
        "extraction_rules": {
            "title": "h1",
            "content": ".content"
        },
        "headers": {},
        "cookies": {}
    }

@pytest.fixture
def sample_user_data():
    """Sample user data for testing."""
    return {
        "username": "newuser",
        "email": "newuser@example.com",
        "full_name": "New User",
        "password": "newpassword123"
    }
